package cn.ouryears.common.exception.handle;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;

import cn.ouryears.common.utils.ExceptionUtils;

/**
 *  A support class for the exception handling. It categorize the exception into 2 categories.<br>
 */

public abstract class ExceptionHandlerSupport implements ExceptionHandler, InitializingBean {
    
    protected final Log logger = LogFactory.getLog(getClass());
    
    /** The exception that should be rethrown, it is the class name only not the real class. */
    protected Set rethrownException;
    
    /** Set that stores the rethrown class */
    private Set rethrownExceptionClass = new HashSet();
    
    /**
     * getter method of rethrownException
     * @return the rethrownException
     */
    public Set getRethrownException() {
        return rethrownException;
    }
    
    /**
     * setter method of rethrownException
     * @param rethrownException the rethrownException to set
     */
    public void setRethrownException(Set rethrownException) {
        this.rethrownException = rethrownException;
    }
    
    /**
     * Set up the rethrownException from String to Class object for later use.
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        
        if(logger.isDebugEnabled()) {
            logger.debug("Set up rethrown exception for exception handler.");
        }
        
        if(rethrownException != null) {
            String _className;
            
            Iterator _it = rethrownException.iterator();
            while(_it.hasNext()) {
                _className = (String)_it.next();
                if(logger.isDebugEnabled()) {
                    logger.debug("Adding exception (" + _className + ") to rethrown set");
                }
                rethrownExceptionClass.add(Class.forName(_className));
            }
            
        }
        else {
            if(logger.isDebugEnabled()) {
                logger.debug("No rethrown exception is specified in xml.");
            }
        }
        
    }
    
    /* (non-Javadoc) Interface Method */
    public boolean canHandle(Throwable t) {
        if(isExceptionRethrown(t)) {
            return false;
        }
        
        return true;
    }
    
    /**
     * Handle exception that is not a instance of UndefineException.
     * 
     * @param t	throwable object.
     * @param obj	object.
     * @return	handled object.
     */
    protected abstract Object handleUndefineException(Throwable t, Object obj);
    
    /**
     * Template method that provide pre-handling for processing Throwable.
     * @param CsidErrors
     * @param obj
     */
    protected void preHandleException(Throwable t, Object obj) {
        
    }
    
    /**
     * Template method that provide post-handing for processing Throwable.
     * @param t
     * @param obj
     */
    protected void postHandleException(Throwable t, Object obj) {
        
    }
    
    /**
     * Check if the Throwable is a Exception. Continue to process if it is a Exception, otherwise return false.<p>
     * Then check if the input Throwable need to rethrown by compare with rethrownExceptionClass set. Return true if need to rethrown, otherwise false.
     * 
     * @param t
     * @return	true if the Throwable need to rethrown, otherwise false.
     */
    protected boolean isExceptionRethrown(Throwable t) {
        
        if(!(t instanceof Exception)) {
            return false;
        }
        
        // Re-thrown the exception without any handling.
        Iterator _it;
        Class _clazz;
        
        if(rethrownExceptionClass != null) {
            
            _it = rethrownExceptionClass.iterator();
            
            while(_it.hasNext()) {
                _clazz = (Class)_it.next();
                if(ExceptionUtils.isExist(t, _clazz)) {
                    if(logger.isDebugEnabled()) {
                        logger.debug("Throwable (" + t
                                        + ") is rethrown. By the specified rethrown class "
                                        + _clazz.getName());
                    }
                    return true;
                }
            }
        }
        else {
            if(logger.isDebugEnabled()) {
                logger.debug("RethrownExceptionClass is null and hence no exception will be rethrown.");
            }
        }
        
        return false;
        
    }
    
    @Override
    public Object handleException(Throwable t, Object obj) {
        // TODO Auto-generated method stub
        return null;
    }
    
}
